package com.example.annolog.utils;

import com.example.annolog.constants.CommonErrorNoConstants;
import com.example.annolog.exception.BaseException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.ScannedGenericBeanDefinition;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.classreading.SimpleMetadataReaderFactory;

import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.HashSet;
import java.util.Set;

/**
 * ClassUtils
 * @author administrator
 * @date 2019-12-12
 */
@Slf4j
public class ClassUtils extends org.apache.commons.lang3.ClassUtils {

    final static Logger logger= LoggerFactory.getLogger(ClassUtils.class);

    public static final String POINT=".";
    public static final String SLASH="/";
    /** 路径匹配转换器 */
    public static final ResourcePatternResolver RESOURCEPATTERNRESOLVER = new PathMatchingResourcePatternResolver();
    
    /** 默认读取factory */
    public static final MetadataReaderFactory METADATAREADERFACTORY = new SimpleMetadataReaderFactory(
        RESOURCEPATTERNRESOLVER);
    
    /**
             * 直接读取属性值，忽略private/protected修饰，不经过getter函数.
     *
     * @param object
             *            目标对象
     * @param fieldName
             *            属性名称
     * @return Object 属性值
     * @throws
             *
     */
    public static Object getFieldValue(final Object object, final String fieldName) throws BaseException
    {
        Field field;
        try {
            field = getDeclaredField(object.getClass(), fieldName);
        }
        catch (Exception e1) {
            String msg = StringUtils.formatStr("Could not find field [{0}] on target [{1}]", fieldName, object);
            throw new BaseException(CommonErrorNoConstants.ERR_COMMON, msg);
        }
        
        makeAccessible(field);
        Object result = null;
        try {
            result = field.get(object);
        }
        catch (IllegalAccessException e) {
            throw new BaseException(CommonErrorNoConstants.ERR_COMMON, "工具类执行异常");
        }
        return result;
    }
    
    /**
             * 给对象中的某个属性设置
     *
     * @param object
             *            对象
     * @param fieldName
             *            属性名称
     * @param fieldValue
             *            属性值
     * @return
     * @throws NoSuchFieldException
             *             异常
     */
    public static Object setFieldValue(final Object object, final String fieldName,
                                       final Object fieldValue) throws NoSuchFieldException, BaseException {
        Field field = null;
        try {
            field = getDeclaredField(object.getClass(), fieldName);
        }
        catch (Exception e1) {
            String msg = StringUtils.formatStr("Could not find field [{0}] on target [{1}]", fieldName, object);
            throw new BaseException(CommonErrorNoConstants.ERR_COMMON, msg);
        } catch (BaseException e) {
            e.printStackTrace();
        }

        makeAccessible(field);
        Object result = null;
        try {
            field.set(object, fieldValue);
        }
        catch (IllegalAccessException e) {
            throw new BaseException(CommonErrorNoConstants.ERR_COMMON, "工具类执行异常");
        }
        return result;
    }
    
    /**
             * 循环向上转型,获取对象的DeclaredField.
     * 
     * @param clazz
             *            目标class
     * @param propertyName
             *            属性名称
     * @return Field 字段对象
     * @throws BaseException
             *             异常
     */
    public static Field getDeclaredField(Class<?> clazz, String propertyName) throws BaseException
    {
        for (Class<?> superClass = clazz; superClass != Object.class; superClass = superClass.getSuperclass()) {
            try {
                return superClass.getDeclaredField(propertyName);
            }
            catch (NoSuchFieldException e) {
                logger.warn("获取属性参数异常");
                logger.warn("code={},msg={}",CommonErrorNoConstants.ERR_COMMON, "获取属性值失败", e);
            }
        }
        String msg = StringUtils.formatStr("No such field: {0} '.'{1}", clazz.getName(), propertyName);
        throw new BaseException(CommonErrorNoConstants.ERR_COMMON, msg);
    }
    
    /**
             * 强行设置Field可访问.
     * 
     * @param field
             *            目标字段
     */
    private static void makeAccessible(final Field field)
    {
        if (!Modifier.isPublic(field.getModifiers()) || !Modifier.isPublic(field.getDeclaringClass().getModifiers())) {
            field.setAccessible(true);
        }
    }
    
    /**
             * 获取某一接口的所有实现类.
     *
     * @param path
             *            获取路径
     * @param superClass
             *            接口class
     * @return
     * @throws IOException
             *             异常
     * @throws ClassNotFoundException
             *             异常
     */
    public static Set<Class<?>> getClassSet(String path, Class<?> superClass) throws IOException, ClassNotFoundException
    {
        if (path.endsWith(POINT)) {
            path = path.substring(0, path.length() - 1);
        }
        path = path.replace(".", "/");
        if (path.startsWith(SLASH)) {
            path = path.substring(1);
        }
        String locationPattern = (new StringBuilder()).append("classpath*:").append(path).append('/')
            .append("**/*.class").toString();
        Resource[] resources = RESOURCEPATTERNRESOLVER.getResources(locationPattern);
        if (resources != null) {
            logger.trace("扫描路径{}包含类数量{}", path, resources.length);
        }
        Set<Class<?>> set = new HashSet<Class<?>>();
        if (ArrayUtils.isNotEmpty(resources)) {
            for (Resource resource : resources) {
                if (resource.isReadable()) {
                    MetadataReader metadataReader = METADATAREADERFACTORY.getMetadataReader(resource);
                    ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
                    sbd.setResource(resource);
                    sbd.setSource(resource);
                    Class<?> clazz = Thread.currentThread().getContextClassLoader().loadClass(sbd.getBeanClassName());
                    if (superClass != null && superClass != clazz && superClass.isAssignableFrom(clazz)) {
                        set.add(Thread.currentThread().getContextClassLoader().loadClass(sbd.getBeanClassName()));
                    }
                }
            }
        }
        return set;
    }
    
    /**
             * 判断该对象所有属性是否为空，所有属性为空返回true,有一个对象不为空则返回false
     * 
     * @param obj
             *            判断的对象
     * @return 所有属性为空返回:true,有一个对象不为空则返回:false
     */
    @SuppressWarnings("rawtypes")
    public static Boolean isAllFieldNull(Object obj) throws BaseException {
        Boolean flag = true;
        Class stuCla = obj.getClass();
        Field[] fs = stuCla.getDeclaredFields();
        for (Field fl : fs) {
            Object val = ReflectUtils.getFieldValue(obj, fl.getName());
            if (null != val) {
                flag = false;
                break;
            }
        }
        return flag;
    }
}
